/***************************************************************
 * Name:      IrReaderMain.cpp
 * Purpose:   Client of IrReader
 * Author:    Jimmy F.Klarke (isjfk@163.com)
 * Created:   2009-08-15
 * Copyright: Jimmy F.Klarke (http://www.isjfk.org)
 * License:
 **************************************************************/

#include <list>
#include <string>

#include <wx/msgdlg.h>
#include <wx/confbase.h>
#include <wx/fileconf.h>

#include "IrReaderMain.h"
#include "IrReaderClient.h"
#include "IrReaderClientWindowsImpl.h"

//(*InternalHeaders(IrReaderFrame)
#include <wx/settings.h>
#include <wx/font.h>
#include <wx/intl.h>
#include <wx/string.h>
//*)

//helper functions
enum wxbuildinfoformat {
    short_f, long_f };

wxString wxbuildinfo(wxbuildinfoformat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == long_f )
    {
#if defined(__WXMSW__)
        wxbuild << _T("-Windows");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode build");
#else
        wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
    }

    return wxbuild;
}

//(*IdInit(IrReaderFrame)
const long IrReaderFrame::ID_STATICTEXT1 = wxNewId();
const long IrReaderFrame::ID_CHOICE_PORT = wxNewId();
const long IrReaderFrame::ID_STATICTEXT2 = wxNewId();
const long IrReaderFrame::ID_CHOICE_DATABITSIZE = wxNewId();
const long IrReaderFrame::ID_STATICTEXT3 = wxNewId();
const long IrReaderFrame::ID_CHOICE_PARITY = wxNewId();
const long IrReaderFrame::ID_STATICTEXT4 = wxNewId();
const long IrReaderFrame::ID_CHOICE_BAUD = wxNewId();
const long IrReaderFrame::ID_STATICTEXT5 = wxNewId();
const long IrReaderFrame::ID_CHOICESTOPBITSIZE = wxNewId();
const long IrReaderFrame::ID_STATICTEXT6 = wxNewId();
const long IrReaderFrame::ID_CHOICE_FLOWCTRL = wxNewId();
const long IrReaderFrame::ID_CHECKBOX_WRITESIGLEN = wxNewId();
const long IrReaderFrame::ID_CHECKBOX_OUTHEX = wxNewId();
const long IrReaderFrame::ID_CHECKBOX_OUTBINARY = wxNewId();
const long IrReaderFrame::ID_BUTTON_START = wxNewId();
const long IrReaderFrame::ID_BUTTON_STOP = wxNewId();
const long IrReaderFrame::ID_BUTTON_CLEAROUTPUT = wxNewId();
const long IrReaderFrame::ID_BUTTON_RESET = wxNewId();
const long IrReaderFrame::ID_BUTTON_EXIT = wxNewId();
const long IrReaderFrame::ID_TEXTCTRL_IRSIGNAL = wxNewId();
const long IrReaderFrame::ID_TEXTCTRL_IRSIGNALBINARY = wxNewId();
const long IrReaderFrame::ID_PANEL_MAIN = wxNewId();
const long IrReaderFrame::ID_STATUSBAR = wxNewId();
//*)

const long IrReaderFrame::ID_CLIENT_STOP = wxNewId();
const long IrReaderFrame::ID_CLIENT_DATA = wxNewId();

BEGIN_EVENT_TABLE(IrReaderFrame,wxFrame)
    //(*EventTable(IrReaderFrame)
    //*)
END_EVENT_TABLE()

IrReaderFrame::IrReaderFrame(wxWindow* parent,wxWindowID id)
{
    //(*Initialize(IrReaderFrame)
    wxStaticBoxSizer* StaticBoxSizer4;
    wxFlexGridSizer* FlexGridSizer3;
    wxFlexGridSizer* FlexGridSizer2;
    wxBoxSizer* BoxSizer2;
    wxStaticBoxSizer* StaticBoxSizer3;
    wxStaticBoxSizer* StaticBoxSizer1;
    wxFlexGridSizer* FlexGridSizer1;
    wxBoxSizer* BoxSizer3;
    wxStaticBoxSizer* StaticBoxSizer5;
    
    Create(parent, wxID_ANY, _("IR Reader Client"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("wxID_ANY"));
    SetClientSize(wxSize(600,450));
    PanelMain = new wxPanel(this, ID_PANEL_MAIN, wxPoint(96,152), wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL_MAIN"));
    outputBoxSizer = new wxBoxSizer(wxVERTICAL);
    StaticBoxSizer1 = new wxStaticBoxSizer(wxHORIZONTAL, PanelMain, _("Config"));
    BoxSizer2 = new wxBoxSizer(wxHORIZONTAL);
    StaticBoxSizer3 = new wxStaticBoxSizer(wxHORIZONTAL, PanelMain, _("Serial"));
    FlexGridSizer1 = new wxFlexGridSizer(2, 6, 0, 0);
    StaticText1 = new wxStaticText(PanelMain, ID_STATICTEXT1, _("Port:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
    FlexGridSizer1->Add(StaticText1, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 3);
    portChoice = new wxChoice(PanelMain, ID_CHOICE_PORT, wxDefaultPosition, wxSize(60,-1), 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_PORT"));
    portChoice->SetSelection( portChoice->Append(_("COM1")) );
    portChoice->Append(_("COM2"));
    portChoice->Append(_("COM3"));
    portChoice->Append(_("COM4"));
    FlexGridSizer1->Add(portChoice, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticText2 = new wxStaticText(PanelMain, ID_STATICTEXT2, _("Data:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT2"));
    FlexGridSizer1->Add(StaticText2, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 3);
    dataBitSizeChoice = new wxChoice(PanelMain, ID_CHOICE_DATABITSIZE, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_DATABITSIZE"));
    dataBitSizeChoice->Append(_("5"));
    dataBitSizeChoice->Append(_("6"));
    dataBitSizeChoice->Append(_("7"));
    dataBitSizeChoice->SetSelection( dataBitSizeChoice->Append(_("8")) );
    FlexGridSizer1->Add(dataBitSizeChoice, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticText3 = new wxStaticText(PanelMain, ID_STATICTEXT3, _("Parity:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT3"));
    FlexGridSizer1->Add(StaticText3, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 3);
    parityChoice = new wxChoice(PanelMain, ID_CHOICE_PARITY, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_PARITY"));
    parityChoice->SetSelection( parityChoice->Append(_("None")) );
    parityChoice->Append(_("Odd"));
    parityChoice->Append(_("Even"));
    parityChoice->Append(_("Mark"));
    parityChoice->Append(_("Space"));
    FlexGridSizer1->Add(parityChoice, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticText4 = new wxStaticText(PanelMain, ID_STATICTEXT4, _("Baud:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT4"));
    FlexGridSizer1->Add(StaticText4, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 3);
    baudChoice = new wxChoice(PanelMain, ID_CHOICE_BAUD, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_BAUD"));
    baudChoice->Append(_("110"));
    baudChoice->Append(_("300"));
    baudChoice->Append(_("600"));
    baudChoice->Append(_("1200"));
    baudChoice->Append(_("2400"));
    baudChoice->Append(_("4800"));
    baudChoice->SetSelection( baudChoice->Append(_("9600")) );
    baudChoice->Append(_("14400"));
    baudChoice->Append(_("19200"));
    baudChoice->Append(_("38400"));
    baudChoice->Append(_("57600"));
    baudChoice->Append(_("115200"));
    baudChoice->Append(_("128000"));
    baudChoice->Append(_("256000"));
    FlexGridSizer1->Add(baudChoice, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticText5 = new wxStaticText(PanelMain, ID_STATICTEXT5, _("Stop:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT5"));
    FlexGridSizer1->Add(StaticText5, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 3);
    stopBitSizeChoice = new wxChoice(PanelMain, ID_CHOICESTOPBITSIZE, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICESTOPBITSIZE"));
    stopBitSizeChoice->SetSelection( stopBitSizeChoice->Append(_("1")) );
    stopBitSizeChoice->Append(_("1.5"));
    stopBitSizeChoice->Append(_("2"));
    FlexGridSizer1->Add(stopBitSizeChoice, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticText6 = new wxStaticText(PanelMain, ID_STATICTEXT6, _("Flow:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT6"));
    FlexGridSizer1->Add(StaticText6, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 3);
    flowCtrlChoice = new wxChoice(PanelMain, ID_CHOICE_FLOWCTRL, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_FLOWCTRL"));
    flowCtrlChoice->SetSelection( flowCtrlChoice->Append(_("None")) );
    flowCtrlChoice->Append(_("Hardware"));
    flowCtrlChoice->Append(_("Software"));
    FlexGridSizer1->Add(flowCtrlChoice, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticBoxSizer3->Add(FlexGridSizer1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    BoxSizer2->Add(StaticBoxSizer3, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticBoxSizer5 = new wxStaticBoxSizer(wxHORIZONTAL, PanelMain, _("IR Reader MCU"));
    FlexGridSizer3 = new wxFlexGridSizer(1, 1, 0, 0);
    writeSigLenCheckBox = new wxCheckBox(PanelMain, ID_CHECKBOX_WRITESIGLEN, _("Write Signal Length"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX_WRITESIGLEN"));
    writeSigLenCheckBox->SetValue(true);
    FlexGridSizer3->Add(writeSigLenCheckBox, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 3);
    StaticBoxSizer5->Add(FlexGridSizer3, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    BoxSizer2->Add(StaticBoxSizer5, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticBoxSizer4 = new wxStaticBoxSizer(wxHORIZONTAL, PanelMain, _("Output"));
    FlexGridSizer2 = new wxFlexGridSizer(2, 1, 0, 0);
    outputHexCheckBox = new wxCheckBox(PanelMain, ID_CHECKBOX_OUTHEX, _("HEX"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX_OUTHEX"));
    outputHexCheckBox->SetValue(false);
    FlexGridSizer2->Add(outputHexCheckBox, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 3);
    outputBinaryCheckBox = new wxCheckBox(PanelMain, ID_CHECKBOX_OUTBINARY, _("BINARY"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX_OUTBINARY"));
    outputBinaryCheckBox->SetValue(true);
    FlexGridSizer2->Add(outputBinaryCheckBox, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 3);
    StaticBoxSizer4->Add(FlexGridSizer2, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    BoxSizer2->Add(StaticBoxSizer4, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    StaticBoxSizer1->Add(BoxSizer2, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
    outputBoxSizer->Add(StaticBoxSizer1, 0, wxTOP|wxLEFT|wxRIGHT|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4);
    BoxSizer3 = new wxBoxSizer(wxHORIZONTAL);
    startButton = new wxButton(PanelMain, ID_BUTTON_START, _("&Start"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_START"));
    startButton->SetDefault();
    startButton->SetFocus();
    BoxSizer3->Add(startButton, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    stopButton = new wxButton(PanelMain, ID_BUTTON_STOP, _("Sto&p"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_STOP"));
    stopButton->Disable();
    BoxSizer3->Add(stopButton, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    BoxSizer3->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    clearOutputButton = new wxButton(PanelMain, ID_BUTTON_CLEAROUTPUT, _("&Clear Output"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_CLEAROUTPUT"));
    BoxSizer3->Add(clearOutputButton, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    resetButton = new wxButton(PanelMain, ID_BUTTON_RESET, _("&Reset"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_RESET"));
    BoxSizer3->Add(resetButton, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    exitButton = new wxButton(PanelMain, ID_BUTTON_EXIT, _("E&xit"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_EXIT"));
    BoxSizer3->Add(exitButton, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    outputBoxSizer->Add(BoxSizer3, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    irSignalHexStaticBoxSizer = new wxStaticBoxSizer(wxHORIZONTAL, PanelMain, _("IR Signal - HEX"));
    irSignalHexTextCtrl = new wxTextCtrl(PanelMain, ID_TEXTCTRL_IRSIGNAL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_AUTO_SCROLL|wxTE_MULTILINE|wxTE_RICH2|wxSUNKEN_BORDER, wxDefaultValidator, _T("ID_TEXTCTRL_IRSIGNAL"));
    wxFont irSignalHexTextCtrlFont = wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT);
    if ( !irSignalHexTextCtrlFont.Ok() ) irSignalHexTextCtrlFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
    irSignalHexTextCtrlFont.SetPointSize((int)(irSignalHexTextCtrlFont.GetPointSize() * 1.000000));
    irSignalHexTextCtrl->SetFont(irSignalHexTextCtrlFont);
    irSignalHexStaticBoxSizer->Add(irSignalHexTextCtrl, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    outputBoxSizer->Add(irSignalHexStaticBoxSizer, 1, wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4);
    irSignalBinaryStaticBoxSizer = new wxStaticBoxSizer(wxHORIZONTAL, PanelMain, _("IR Signal - BINARY"));
    irSignalBinaryTextCtrl = new wxTextCtrl(PanelMain, ID_TEXTCTRL_IRSIGNALBINARY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_AUTO_SCROLL|wxTE_MULTILINE|wxTE_RICH2|wxSUNKEN_BORDER, wxDefaultValidator, _T("ID_TEXTCTRL_IRSIGNALBINARY"));
    wxFont irSignalBinaryTextCtrlFont = wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT);
    if ( !irSignalBinaryTextCtrlFont.Ok() ) irSignalBinaryTextCtrlFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
    irSignalBinaryTextCtrlFont.SetPointSize((int)(irSignalBinaryTextCtrlFont.GetPointSize() * 1.000000));
    irSignalBinaryTextCtrl->SetFont(irSignalBinaryTextCtrlFont);
    irSignalBinaryStaticBoxSizer->Add(irSignalBinaryTextCtrl, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);
    outputBoxSizer->Add(irSignalBinaryStaticBoxSizer, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4);
    PanelMain->SetSizer(outputBoxSizer);
    outputBoxSizer->Fit(PanelMain);
    outputBoxSizer->SetSizeHints(PanelMain);
    statusBar = new wxStatusBar(this, ID_STATUSBAR, wxST_SIZEGRIP, _T("ID_STATUSBAR"));
    int __wxStatusBarWidths_1[1] = { -1 };
    int __wxStatusBarStyles_1[1] = { wxSB_NORMAL };
    statusBar->SetFieldsCount(1,__wxStatusBarWidths_1);
    statusBar->SetStatusStyles(1,__wxStatusBarStyles_1);
    SetStatusBar(statusBar);
    Center();
    
    Connect(ID_CHECKBOX_OUTHEX,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnOutputHexCheckBoxClick);
    Connect(ID_CHECKBOX_OUTBINARY,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnOutputBinaryCheckBoxClick);
    Connect(ID_BUTTON_START,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnStartButtonClick);
    Connect(ID_BUTTON_STOP,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnStopButtonClick);
    Connect(ID_BUTTON_CLEAROUTPUT,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnClearOutputButtonClick);
    Connect(ID_BUTTON_RESET,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnResetButtonClick);
    Connect(ID_BUTTON_EXIT,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&IrReaderFrame::OnExitButtonClick);
    Connect(wxID_ANY,wxEVT_CLOSE_WINDOW,(wxObjectEventFunction)&IrReaderFrame::OnClose);
    //*)

    Connect(ID_CLIENT_DATA, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&IrReaderFrame::OnIrReaderClientData);
    Connect(ID_CLIENT_STOP, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&IrReaderFrame::OnIrReaderClientStop);

    // init communication and mcu configuration
    irReaderClient = new IrReaderClientWindowsImpl();
    initSerialParam(irReaderClient);

    // initial ui configuration
    SetMinSize(GetSize());
    Maximize(true);

    wxFileConfig* config = new wxFileConfig();
    config->SetRecordDefaults(true);
    wxConfigBase::DontCreateOnDemand();
    wxConfigBase::Set(config);

    readConfig();
    writeConfig();
}

IrReaderFrame::~IrReaderFrame()
{
    //(*Destroy(IrReaderFrame)
    //*)

    delete irReaderClient;
}

void IrReaderFrame::OnQuit(wxCommandEvent& event)
{
    Close();
}

void IrReaderFrame::OnAbout(wxCommandEvent& event)
{
    wxString msg = wxbuildinfo(long_f);
    int width = 0;
    int height = 0;
    GetSize(&width, &height);
    msg = msg + wxString::Format(_T("%dx%d"), width, height);
    wxMessageBox(msg, _("Welcome to..."));
}

void IrReaderFrame::OnOutputHexCheckBoxClick(wxCommandEvent& event)
{
    showOutputHex(event.IsChecked());
}

void IrReaderFrame::OnOutputBinaryCheckBoxClick(wxCommandEvent& event)
{
    showOutputBinary(event.IsChecked());
}

void IrReaderFrame::OnStartButtonClick(wxCommandEvent& event)
{
    try {
        // set client config
        irReaderClient->setMcuWriteSigLen(writeSigLenCheckBox->GetValue());

        // open client
        irReaderClient->openClient(
                string(portChoice->GetStringSelection().mb_str()),
                string(baudChoice->GetStringSelection().mb_str()),
                string(dataBitSizeChoice->GetStringSelection().mb_str()),
                string(stopBitSizeChoice->GetStringSelection().mb_str()),
                string(parityChoice->GetStringSelection().mb_str()),
                string(flowCtrlChoice->GetStringSelection().mb_str()));

        // start client reader thread
        clientThread = new ClientThread(this, irReaderClient);
        if (clientThread->Create() != wxTHREAD_NO_ERROR) {
            throw new IrReaderClientException("Create thread error!");
        }
        if (clientThread->Run() != wxTHREAD_NO_ERROR) {
            throw new IrReaderClientException("Start thread error!");
        }

        // set signal size param
        signalSize = 0;
        signalReceived = 0;

        // set ui
        portChoice->Enable(false);
        baudChoice->Enable(false);
        dataBitSizeChoice->Enable(false);
        stopBitSizeChoice->Enable(false);
        parityChoice->Enable(false);
        flowCtrlChoice->Enable(false);
        writeSigLenCheckBox->Enable(false);
        startButton->Enable(false);
        resetButton->Enable(false);
        stopButton->Enable(true);
    } catch (IrReaderClientException e) {
        wxMessageBox(wxString::FromUTF8(e.getMsg().c_str()), _("Error"));
    }
}

void IrReaderFrame::OnStopButtonClick(wxCommandEvent& event)
{
    stopButton->Enable(false);
    clientThread->Delete();
}

void IrReaderFrame::OnClearOutputButtonClick(wxCommandEvent& event)
{
    irSignalBinaryTextCtrl->Clear();
    irSignalHexTextCtrl->Clear();
}

void IrReaderFrame::OnResetButtonClick(wxCommandEvent& event)
{
    resetConfig();
}

void IrReaderFrame::OnExitButtonClick(wxCommandEvent& event)
{
    if (stopButton->IsEnabled()) {
        clientThread->Delete();
    }
    Close();
}

void IrReaderFrame::OnClose(wxCloseEvent& event)
{
    writeConfig();
    this->Destroy();
}

void IrReaderFrame::OnIrReaderClientData(wxCommandEvent& event)
{
    try {
        vector<unsigned char> signals = clientThread->popSignals();
        writeSignals(signals);
    } catch (IrReaderClientException e) {
        statusBar->SetStatusText(wxString::FromUTF8(e.getMsg().c_str()), 0);
    }
}

void IrReaderFrame::OnIrReaderClientStop(wxCommandEvent& event)
{
    // set ui
    portChoice->Enable(true);
    baudChoice->Enable(true);
    dataBitSizeChoice->Enable(true);
    stopBitSizeChoice->Enable(true);
    parityChoice->Enable(true);
    flowCtrlChoice->Enable(true);
    writeSigLenCheckBox->Enable(true);
    startButton->Enable(true);
    resetButton->Enable(true);
    stopButton->Enable(false);

    // client thread will delete themselfs
    clientThread = NULL;

    // close client
    try {
        irReaderClient->closeClient();
    } catch (IrReaderClientException e) {
        statusBar->SetStatusText(wxString::FromUTF8(e.getMsg().c_str()), 0);
    }
}

void IrReaderFrame::showOutputHex(bool show)
{
    outputBinaryCheckBox->Enable(show);
    outputBoxSizer->Show(irSignalHexStaticBoxSizer, show);
    outputBoxSizer->Layout();
}

void IrReaderFrame::showOutputBinary(bool show)
{
    outputHexCheckBox->Enable(show);
    outputBoxSizer->Show(irSignalBinaryStaticBoxSizer, show);
    outputBoxSizer->Layout();
}

void IrReaderFrame::initSerialParam(IrReaderClient* irReaderClient)
{
    using namespace std;

    portChoice->Clear();
    vector<string> portList = irReaderClient->getPortList();
    if (portList.empty()) {
        portChoice->Enable(false);
        startButton->Enable(false);
        stopButton->Enable(false);
        resetButton->Enable(false);
        statusBar->SetStatusText(_T("No serial port found!"), 0);
    } else {
        for (size_t i = 0; i < portList.size(); i++) {
            portChoice->Append(wxString::FromUTF8(portList[i].c_str()));
        }
        portChoice->SetStringSelection(getPortDefault());
    }
}

wxString IrReaderFrame::getPortDefault()
{
    return wxString::FromUTF8(irReaderClient->getPortDefault().c_str());
}

wxString IrReaderFrame::getBaudDefault()
{
    return wxString::FromUTF8(irReaderClient->getBaudDefault().c_str());
}

wxString IrReaderFrame::getDataBitSizeDefault()
{
    return wxString::FromUTF8(irReaderClient->getDataBitSizeDefault().c_str());
}

wxString IrReaderFrame::getStopBitSizeDefault()
{
    return wxString::FromUTF8(irReaderClient->getStopBitSizeDefault().c_str());
}

wxString IrReaderFrame::getParityDefault()
{
    return wxString::FromUTF8(irReaderClient->getParityDefault().c_str());
}

wxString IrReaderFrame::getFlowCtrlDefault()
{
    return wxString::FromUTF8(irReaderClient->getFlowCtrlDefault().c_str());
}

void IrReaderFrame::readConfig()
{
    wxFileConfig* config = (wxFileConfig*) wxConfigBase::Get(false);
    if (config != NULL) {
        wxString port = config->Read(CONFIG_SERIAL_PORT, getPortDefault());
        if (portChoice->FindString(port) != wxNOT_FOUND) {
            portChoice->SetStringSelection(port);
        }

        wxString baud = config->Read(CONFIG_SERIAL_BAUD, getBaudDefault());
        if (baudChoice->FindString(baud) != wxNOT_FOUND) {
            baudChoice->SetStringSelection(baud);
        }

        wxString dataBitSize = config->Read(CONFIG_SERIAL_DATABITSIZE, getDataBitSizeDefault());
        if (dataBitSizeChoice->FindString(dataBitSize) != wxNOT_FOUND) {
            dataBitSizeChoice->SetStringSelection(dataBitSize);
        }

        wxString stopBitSize = config->Read(CONFIG_SERIAL_STOPBITSIZE, getStopBitSizeDefault());
        if (stopBitSizeChoice->FindString(stopBitSize) != wxNOT_FOUND) {
            stopBitSizeChoice->SetStringSelection(stopBitSize);
        }

        wxString parity = config->Read(CONFIG_SERIAL_PARITY, getParityDefault());
        if (parityChoice->FindString(parity) != wxNOT_FOUND) {
            parityChoice->SetStringSelection(parity);
        }

        wxString flowCtrl = config->Read(CONFIG_SERIAL_FLOWCTRL, getFlowCtrlDefault());
        if (flowCtrlChoice->FindString(flowCtrl) != wxNOT_FOUND) {
            flowCtrlChoice->SetStringSelection(flowCtrl);
        }

        bool writeSigLen;
        config->Read(CONFIG_MCU_WRITESIGLEN, &writeSigLen, CONFIG_MCU_WRITESIGLEN_DEFAULT);
        writeSigLenCheckBox->SetValue(writeSigLen);

        bool outputHex;
        bool outputBinary;
        config->Read(CONFIG_OUTPUT_HEX, &outputHex, CONFIG_OUTPUT_HEX_DEFAULT);
        config->Read(CONFIG_OUTPUT_BINARY, &outputBinary, CONFIG_OUTPUT_BINARY_DEFAULT);
        if (!outputHex && !outputBinary) {
            outputHex = CONFIG_OUTPUT_HEX_DEFAULT;
            outputBinary = CONFIG_OUTPUT_BINARY_DEFAULT;
        }
        outputHexCheckBox->SetValue(outputHex);
        outputBinaryCheckBox->SetValue(outputBinary);
        showOutputHex(outputHex);
        showOutputBinary(outputBinary);
    }
}

void IrReaderFrame::writeConfig()
{
    wxFileConfig* config = (wxFileConfig*) wxConfigBase::Get(false);
    if (config != NULL) {
        config->Write(CONFIG_SERIAL_PORT, portChoice->IsEmpty() ? getPortDefault() : portChoice->GetStringSelection());
        config->Write(CONFIG_SERIAL_BAUD, baudChoice->GetStringSelection());
        config->Write(CONFIG_SERIAL_DATABITSIZE, dataBitSizeChoice->GetStringSelection());
        config->Write(CONFIG_SERIAL_STOPBITSIZE, stopBitSizeChoice->GetStringSelection());
        config->Write(CONFIG_SERIAL_PARITY, parityChoice->GetStringSelection());
        config->Write(CONFIG_SERIAL_FLOWCTRL, flowCtrlChoice->GetStringSelection());
        config->Write(CONFIG_MCU_WRITESIGLEN, writeSigLenCheckBox->GetValue());
        config->Write(CONFIG_OUTPUT_HEX, outputHexCheckBox->GetValue());
        config->Write(CONFIG_OUTPUT_BINARY, outputBinaryCheckBox->GetValue());
        config->Flush();
    }
}

void IrReaderFrame::resetConfig()
{
    if (!portChoice->IsEmpty()) {
        portChoice->SetStringSelection(getPortDefault());
    }
    baudChoice->SetStringSelection(getBaudDefault());
    dataBitSizeChoice->SetStringSelection(getDataBitSizeDefault());
    stopBitSizeChoice->SetStringSelection(getStopBitSizeDefault());
    parityChoice->SetStringSelection(getParityDefault());
    flowCtrlChoice->SetStringSelection(getFlowCtrlDefault());
    writeSigLenCheckBox->SetValue(CONFIG_MCU_WRITESIGLEN_DEFAULT);
    outputHexCheckBox->SetValue(CONFIG_OUTPUT_HEX_DEFAULT);
    outputBinaryCheckBox->SetValue(CONFIG_OUTPUT_BINARY_DEFAULT);

    showOutputHex(outputHexCheckBox->GetValue());
    showOutputBinary(outputBinaryCheckBox->GetValue());

    writeConfig();
}

void IrReaderFrame::writeSignals(vector<unsigned char> signals)
{
    for (size_t i = 0; i < signals.size(); i++) {
        if (writeSigLenCheckBox->GetValue()) {
            if ((signalReceived++) == 0) {
                signalSize = signals[i];
                continue;
            }
        }

        if (outputBinaryCheckBox->GetValue()) {
            for (int shift = 7; shift >= 0; shift--) {
                if (signals[i] & (1 << shift)) {
                    irSignalBinaryTextCtrl->AppendText(_T("1"));
                } else {
                    irSignalBinaryTextCtrl->AppendText(_T("0"));
                }
            }
        }
        if (outputHexCheckBox->GetValue()) {
            irSignalHexTextCtrl->AppendText(wxString::Format(_T("%02X"), (int) signals[i]));
        }

        if (writeSigLenCheckBox->GetValue() && (signalReceived > signalSize)) {
            signalReceived = 0;
            irSignalBinaryTextCtrl->AppendText(_T("\n"));
            irSignalHexTextCtrl->AppendText(_T("\n"));
        } else {
            if (outputBinaryCheckBox->GetValue()) {
                irSignalBinaryTextCtrl->AppendText(_T(" "));
            }
            if (outputHexCheckBox->GetValue()) {
                irSignalHexTextCtrl->AppendText(_T(" "));
            }
        }
    }
}

ClientThread::ClientThread(wxFrame* frame, IrReaderClient* client)
{
    this->frame = frame;
    this->client = client;
}

void* ClientThread::Entry()
{
    while (true) {
        if (TestDestroy()) {
            break;
        }

        try {
            pushSignals(client->read());
        } catch (IrReaderClientException e) {
            break;
        }

        wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, IrReaderFrame::ID_CLIENT_DATA);
        wxPostEvent(frame, event);
    }

    return NULL;
}

void ClientThread::OnExit()
{
    wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, IrReaderFrame::ID_CLIENT_STOP);
    wxPostEvent(frame, event);
}

void ClientThread::pushSignals(vector<unsigned char> signals)
{
    wxMutexLocker locker(signalLock);

    for (size_t i = 0; i < signals.size(); i++) {
        this->signals.push_back(signals[i]);
    }
}

vector<unsigned char> ClientThread::popSignals()
{
    wxMutexLocker locker(signalLock);

    vector<unsigned char> signalsCopy(signals.begin(), signals.end());
    signals.clear();

    return signalsCopy;
}
